module i2c_tx_rom_phy # 
(
    parameter SYS_FREQ = 100000000,
    parameter IIC_FREQ = 100000,
    parameter WAIT_TIME = 10000,
    parameter ROM_WIDTH = 7,
    parameter SLV_ADDR = 7'h60,
    parameter TRI_STATE = 1,
    parameter ROM_DEPTH = 61
)
(
    input wire        sys_clk,
    input wire        sys_rst,

    input wire        send_config_en,

    output wire       iic_scl,
    inout  wire       iic_sda
);

//*********STATE MACHINE PARAMETER*********

reg [2 :0 ] state;

localparam IDLE = 3'd0;
localparam WORD_START = 3'd1;
localparam SEND_WRID = 3'd2;
localparam SEND_ADDR = 3'd3;
localparam SEND_DATA = 3'd4;
localparam WORD_END = 3'd5;
localparam WAIT      = 3'd6;

//*********IIC SDA TRI STATE GATE**********

reg iic_sda_reg;
reg iic_sda_dir;
reg iic_scl_reg;

assign iic_sda = iic_sda_dir ? iic_sda_reg : 1'bz;
assign iic_scl = iic_scl_reg;

//*********DATA ROM INITIAL****************

wire [15 :0 ] rom_data [0 :ROM_DEPTH -1];

    //$readmemh("IIC_ROM.coe",rom_data);
assign rom_data [ 0] = {8'h2,8'h53};
assign rom_data [ 1] = {8'h3,8'h0};
assign rom_data [ 2] = {8'h4,8'h20};
assign rom_data [ 3] = {8'h7,8'h0};
assign rom_data [ 4] = {8'hf,8'h0};
assign rom_data [ 5] = {8'h10,8'hf};
assign rom_data [ 6] = {8'h11,8'hf};
assign rom_data [ 7] = {8'h12,8'h4f};
assign rom_data [ 8] = {8'h13,8'h8c};
assign rom_data [ 9] = {8'h14,8'h8c};
assign rom_data [10] = {8'h15,8'h8c};
assign rom_data [11] = {8'h16,8'h8c};
assign rom_data [12] = {8'h17,8'h8c};
assign rom_data [13] = {8'h1a,8'h0};
assign rom_data [14] = {8'h1b,8'h1};
assign rom_data [15] = {8'h1c,8'h0};
assign rom_data [16] = {8'h1d,8'hd};
assign rom_data [17] = {8'h1e,8'h0};
assign rom_data [18] = {8'h1f,8'h0};
assign rom_data [19] = {8'h20,8'h0};
assign rom_data [20] = {8'h21,8'h0};
assign rom_data [21] = {8'h2a,8'h0};
assign rom_data [22] = {8'h2b,8'h1};
assign rom_data [23] = {8'h2c,8'h0};
assign rom_data [24] = {8'h2d,8'h5};
assign rom_data [25] = {8'h2e,8'h80};
assign rom_data [26] = {8'h2f,8'h0};
assign rom_data [27] = {8'h30,8'h0};
assign rom_data [28] = {8'h31,8'h0};
assign rom_data [29] = {8'h32,8'h0};
assign rom_data [30] = {8'h33,8'h8};
assign rom_data [31] = {8'h34,8'h0};
assign rom_data [32] = {8'h35,8'h2};
assign rom_data [33] = {8'h36,8'hb0};
assign rom_data [34] = {8'h37,8'h0};
assign rom_data [35] = {8'h38,8'h0};
assign rom_data [36] = {8'h39,8'h0};
assign rom_data [37] = {8'h3a,8'h0};
assign rom_data [38] = {8'h3b,8'h1};
assign rom_data [39] = {8'h3c,8'h0};
assign rom_data [40] = {8'h3d,8'h1};
assign rom_data [41] = {8'h3e,8'h0};
assign rom_data [42] = {8'h3f,8'h0};
assign rom_data [43] = {8'h40,8'h0};
assign rom_data [44] = {8'h41,8'h0};
assign rom_data [45] = {8'h5a,8'h0};
assign rom_data [46] = {8'h5b,8'h0};
assign rom_data [47] = {8'h95,8'h0};
assign rom_data [48] = {8'h96,8'h0};
assign rom_data [49] = {8'h97,8'h0};
assign rom_data [50] = {8'h98,8'h0};
assign rom_data [51] = {8'h99,8'h0};
assign rom_data [52] = {8'h9a,8'h0};
assign rom_data [53] = {8'h9b,8'h0};
assign rom_data [54] = {8'ha2,8'h0};
assign rom_data [55] = {8'ha3,8'h0};
assign rom_data [56] = {8'ha4,8'h0};
assign rom_data [57] = {8'ha5,8'h0};
assign rom_data [58] = {8'ha6,8'h0};
assign rom_data [59] = {8'ha7,8'h0};
assign rom_data [60] = {8'hb7,8'h92};

//********IIC CLOCK DIVIDER AND PLUSE GENERATOR*******

reg [31:0] div_cnt;
wire data_tx_pluse;
wire start_end_pluse;
wire scl_pluse;
always @ (posedge sys_clk)
    if(sys_rst)
        div_cnt <= 32'd0;
    else if(state == IDLE || state == WAIT)
        div_cnt <= 32'd0;
    else if(div_cnt == SYS_FREQ/IIC_FREQ - 1)
        div_cnt <= 32'd0;
    else
        div_cnt <= div_cnt + 32'd1;


assign scl_pluse        = div_cnt <  1*SYS_FREQ/IIC_FREQ/2  ? 1'b1 : 1'b0;
assign data_tx_pluse    = div_cnt == 3*SYS_FREQ/IIC_FREQ/4  ? 1'b1 : 1'b0;
assign start_end_pluse  = div_cnt == 1*SYS_FREQ/IIC_FREQ/4  ? 1'b1 : 1'b0;

//*********MAIN STATE MACHINE************************

reg [ROM_WIDTH :0 ] rom_addr;
reg [3 :0 ] byte_cnt;

reg [31:0 ] wait_cnt;

always @ (posedge sys_clk)
    if(sys_rst)
        state <= IDLE;
    else
        case(state)
            IDLE: 
                if(send_config_en == 1'b1)
                    state <= WORD_START;
            WORD_START:
                if(data_tx_pluse == 1'b1)
                    state <= SEND_WRID;
            SEND_WRID:
                if(data_tx_pluse == 1'b1 && byte_cnt == 4'd0)
                    state <= SEND_ADDR;
            SEND_ADDR:
                if(data_tx_pluse == 1'b1 && byte_cnt == 4'd0)
                    state <= SEND_DATA;
            SEND_DATA:
                if(data_tx_pluse == 1'b1 && byte_cnt == 4'd0)
                    state <= WORD_END;
            WORD_END:
                if(iic_sda_reg == 1'b1 && data_tx_pluse == 1'b1)
                    if(rom_addr == ROM_DEPTH - 1)
                        state <= IDLE;
                    else
                        state <= WAIT;
            WAIT:
                if(wait_cnt == WAIT_TIME)
                    state <= WORD_START;
            default: state <= IDLE;
        endcase

//*********IIC SCL OUTPUT************************

reg iic_scl_latch;

always @ (posedge sys_clk)
    if(sys_rst) begin
        iic_scl_latch <= 1'b0;
        iic_scl_reg <= 1'b1;
    end
    else if(state == IDLE) begin
        iic_scl_reg <= 1'b1;
        iic_scl_latch <= 1'b0;
    end
    else if(state == WORD_START)
        iic_scl_reg <= scl_pluse;
    else if(state == SEND_WRID)
        iic_scl_reg <= scl_pluse;
    else if(state == SEND_ADDR)
        iic_scl_reg <= scl_pluse;
    else if(state == SEND_DATA)
        iic_scl_reg <= scl_pluse;
    else if(state == WORD_END)
        if(start_end_pluse || iic_scl_latch) begin
            iic_scl_reg <= 1'b1;
            iic_scl_latch <= 1'b1;
        end
    else if(state == WAIT)
        iic_scl_reg <= 1'b1;


//*********IIC SDA OUTPUT************************    

wire [8 :0 ] send_wrid;
wire [8 :0 ] send_addr;
wire [8 :0 ] send_data;

assign send_wrid = {SLV_ADDR,1'b0,1'b0};
assign send_addr = {rom_data[rom_addr][15:8],1'b0};
assign send_data = {rom_data[rom_addr][7:0],1'b0};

always @ (posedge sys_clk)
    if(sys_rst) begin
        iic_sda_reg <= 1'b1;
        iic_sda_dir <= 1'b1;
    end
    else if(state == IDLE) begin
        iic_sda_reg <= 1'b1;
        iic_sda_dir <= 1'b1;
    end
    else if(state == WORD_START) begin
        iic_sda_dir <= 1'b1;
        if(start_end_pluse)
            iic_sda_reg <= 1'b0;
    end
    else if(state == SEND_WRID) begin
        if(byte_cnt == 4'd0)
            iic_sda_dir <= TRI_STATE;
        else
            iic_sda_dir <= 1'b1;
        iic_sda_reg <= send_wrid[byte_cnt];
    end
    else if(state == SEND_ADDR) begin
        if(byte_cnt == 4'd0)
            iic_sda_dir <= TRI_STATE;
        else
            iic_sda_dir <= 1'b1;
        iic_sda_reg <= send_addr[byte_cnt];
    end
    else if(state == SEND_DATA) begin
        if(byte_cnt == 4'd0)
            iic_sda_dir <= TRI_STATE;
        else
            iic_sda_dir <= 1'b1;
        iic_sda_reg <= send_data[byte_cnt];
    end
    else if(state == WORD_END) begin
        iic_sda_dir <= 1'b1;
        if(data_tx_pluse)
            iic_sda_reg <= 1'b1;
    end
    else if(state == WAIT) begin
        iic_sda_reg <= 1'b1;
        iic_sda_dir <= 1'b1;
    end

//*********WAIT COUNTER**********

always @ (posedge sys_clk)
    if(sys_rst)
        wait_cnt <= 32'd0;
    else if(state == WAIT)
        wait_cnt <= wait_cnt + 32'd1;
    else
        wait_cnt <= 32'd0;

//*********BIT COUNTER**********

always @ (posedge sys_clk)
    if(sys_rst)
        byte_cnt <= 'd0;
    else
        case(state)
            IDLE: byte_cnt <= 'd0;
            WORD_START: byte_cnt <= 'd8;
            SEND_WRID: 
                if(data_tx_pluse && byte_cnt!=0)
                    byte_cnt <= byte_cnt - 'd1;
                else if(data_tx_pluse && byte_cnt==0)
                    byte_cnt <= 'd8;
            SEND_ADDR:
                if(data_tx_pluse && byte_cnt!=0)
                    byte_cnt <= byte_cnt - 'd1;
                else if(data_tx_pluse && byte_cnt==0)
                    byte_cnt <= 'd8;
            SEND_DATA:
                if(data_tx_pluse && byte_cnt!=0)
                    byte_cnt <= byte_cnt - 'd1;
            default: byte_cnt <= 'd0;
        endcase

//*********ROM ADDRESS CONTROLLER**********

always @ (posedge sys_clk)
    if(sys_rst)
        rom_addr <= 'd0;
    else if(state == IDLE)
        rom_addr <= 'd0;
    else if(state == WORD_END && iic_sda_reg == 1'b1 && data_tx_pluse == 1'b1)
        rom_addr <= rom_addr + 'd1;

endmodule